DEFINT A-Z

DIM SHARED N(9999) AS INTEGER,NU(5039) AS INTEGER,T(9999)AS INTEGER
DIM SHARED ASM(119) AS INTEGER,RES(1,15) AS LONG
COMMON SHARED NOCSV,RMAX

PRINT "Initializing...";
DEF SEG=VARSEG(N(0)):P=VARPTR(N(0)):P2=0
FOR I=0 TO 9999
  D0=(I MOD 10):D1=(I\10) MOD 10:D2=(I\100) MOD 10:D3=I\1000
  POKE P+I*2,(D1*16) OR D0:POKE P+I*2+1,(D3*16) OR D2
  IF D0<>D1 AND D0<>D2 AND D0<>D3 AND D1<>D2 AND D1<>D3 AND D2<>D3 THEN
    NU(P2)=N(I):P2=P2+1
  END IF
NEXT I
DEF SEG

DEF SEG=VARSEG(ASM(0))
BLOAD "GSTAT.BIN",VARPTR(ASM(0))
DEF SEG

IF INKEY$="t" THEN CALL TEST

RANDOMIZE TIMER
F$="GSTAT.CSV"
NOCSV=0:RMAX=7

CALL LOADDATA(F$)
CLS
LOCATE 25,1:PRINT "Press Esc to exit";

CALL PRINTSTAT
TS#=TIMER:TP#=TIMER
G0=0:G1=0
WHILE G0>=0 AND G1>=0 AND INKEY$<>CHR$(27)
  TM#=TIMER
  IF TM#<TP# OR TM#-TP#>1 THEN TP#=TM#:CALL PRINTSTAT
  IF TM#<TS# OR TM#-TS#>120 THEN TS#=TM#:CALL SAVEDATA(F$)
  G0=-1:G1=-1
  G0=GUESS:IF G0>0 THEN G1=GUESSM
  IF G0>0 AND G1>0 THEN
    G0=G0-1
    RES(0,G0)=RES(0,G0)+1
    G1=G1-1
    RES(1,G1)=RES(1,G1)+1
    IF G0>RMAX THEN RMAX=G0
    IF G1>RMAX THEN RMAX=G1
  END IF
WEND
CLS:CALL SAVEDATA(F$):END

ErrorHandler:
NOCSV=1
RESUME NEXT

SUB PRINTSTAT
  STATIC SUM0 AS LONG,SUM1 AS LONG
  LOCATE 1,1:PRINT "Guesses     Normal Mastermind"
  SUM0=0:SUM1=0
  FOR I=0 TO RMAX
    PRINT LTRIM$(STR$(I+1));TAB(9);
    PRINT USING "########## ##########";RES(0,I);RES(1,I)
    SUM0=SUM0+RES(0,I):SUM1=SUM1+RES(1,I)
  NEXT I
  PRINT "Total";TAB(9);
  PRINT USING "########## ##########";SUM0;SUM1
END SUB

SUB LOADDATA(FILENAME AS STRING)
  STATIC IDX AS INTEGER,P AS INTEGER
  ON ERROR GOTO ErrorHandler
  OPEN FILENAME FOR INPUT AS #1
  ON ERROR GOTO 0
  IF NOCSV<>0 THEN EXIT SUB
  LINE INPUT #1,S$
  WHILE NOT EOF(1)
    LINE INPUT #1,S$
    S$=LTRIM$(RTRIM$(S$))
    IF S$<>"" THEN
      P=INSTR(S$,","):V$=LEFT$(S$,P-1):S$=MID$(S$,P+1)
      G=VAL(V$)-1
      IF G>RMAX THEN RMAX=G
      P=INSTR(S$,","):V$=LEFT$(S$,P-1):S$=MID$(S$,P+1)
      RES(0,G)=VAL(V$):RES(1,G)=VAL(S$)
    END IF
  WEND
  CLOSE #1
END SUB

SUB SAVEDATA(FILENAME AS STRING)
  STATIC ROWS AS INTEGER
  ROWS=7
  FOR I=8 TO 15
    IF RES(0,I)>0 OR RES(1,I)>0 THEN ROWS=I
  NEXT I
  OPEN FILENAME FOR OUTPUT AS #1
  PRINT #1,"Guesses,Normal,Mastermind"
  FOR I=0 TO ROWS
    PRINT #1,LTRIM$(RTRIM$(STR$(I+1)));
    PRINT #1,",";LTRIM$(RTRIM$(STR$(RES(0,I))));
    PRINT #1,",";LTRIM$(RTRIM$(STR$(RES(1,I))))
  NEXT I
  CLOSE #1
END SUB

FUNCTION GUESS()
  STATIC ANS AS INTEGER,G AS INTEGER,TL AS INTEGER,TIMES AS INTEGER
  ANS=NU(INT(RND*5039)):G=NU(INT(RND*5039))
  IF G=ANS THEN GUESS=1:EXIT FUNCTION
  TL=5040
  DEF SEG=VARSEG(ASM(0))
  CALL ABSOLUTE(BYVAL VARSEG(T(0)),BYVAL VARPTR(T(0)),BYVAL VARSEG(NU(0)),BYVAL VARPTR(NU(0)),TL,BYVAL G,BYVAL ANS,VARPTR(ASM(0)))
  DEF SEG
  TIMES=1
  WHILE TL>1
    G=T(INT(RND*(TL-1))):IF G=ANS THEN GUESS=TIMES+1:EXIT FUNCTION
    DEF SEG=VARSEG(ASM(0))
    CALL ABSOLUTE(BYVAL VARSEG(T(0)),BYVAL VARPTR(T(0)),BYVAL VARSEG(T(0)),BYVAL VARPTR(T(0)),TL,BYVAL G,BYVAL ANS,VARPTR(ASM(0)))
    DEF SEG
    TIMES=TIMES+1
  WEND
  IF T(0)<>ANS OR TL=0 THEN
    PRINT "Normal Error";HEX$(T(0)),HEX$(ANS);TL:END
  END IF
  GUESS=TIMES+1
END FUNCTION

FUNCTION GUESSM()
  STATIC ANS AS INTEGER,G AS INTEGER,TL AS INTEGER,TIMES AS INTEGER
  ANS=N(INT(RND*9999)):G=N(INT(RND*9999))
  IF ANS=G THEN GUESSM=1:EXIT FUNCTION
  TL=10000
  DEF SEG=VARSEG(ASM(0))
  CALL ABSOLUTE(BYVAL VARSEG(T(0)),BYVAL VARPTR(T(0)),BYVAL VARSEG(N(0)),BYVAL VARPTR(N(0)),TL,BYVAL G,BYVAL ANS,VARPTR(ASM(0)))
  DEF SEG
  TIMES=1
  WHILE TL>1
    G=T(INT(RND*(TL-1))):IF G=ANS THEN GUESSM=TIMES+1:EXIT FUNCTION
    DEF SEG=VARSEG(ASM(0))
    CALL ABSOLUTE(BYVAL VARSEG(T(0)),BYVAL VARPTR(T(0)),BYVAL VARSEG(T(0)),BYVAL VARPTR(T(0)),TL,BYVAL G,BYVAL ANS,VARPTR(ASM(0)))
    DEF SEG
    TIMES=TIMES+1
  WEND
  IF T(0)<>ANS OR TL=0 THEN
    PRINT "Mastermind Error";HEX$(T(0)),HEX$(ANS);TL:END
  END IF
  GUESSM=TIMES+1
END FUNCTION

SUB TEST
  PRINT "TESTING"
  IF COMP(&H1234,&H1243)<>&H22 THEN PRINT "COMP ERR":END

  TL=5040
  DEF SEG=VARSEG(ASM(0))
  CALL ABSOLUTE(BYVAL VARSEG(T(0)),BYVAL VARPTR(T(0)),BYVAL VARSEG(NU(0)),BYVAL VARPTR(NU(0)),TL,BYVAL &H5678,BYVAL &H1234,VARPTR(ASM(0)))
  DEF SEG
  PRINT "Normal filter check"
  CALL CHECKFILTER(&H1234,&H5678,5040,NU(),T(),TL)
  PRINT "Normal filter ok"

  TL=10000
  DEF SEG=VARSEG(ASM(0))
  CALL ABSOLUTE(BYVAL VARSEG(T(0)),BYVAL VARPTR(T(0)),BYVAL VARSEG(N(0)),BYVAL VARPTR(N(0)),TL,BYVAL &H1234,BYVAL &H0000,VARPTR(ASM(0)))
  DEF SEG
  CALL CHECKNUMPOOL
  PRINT "Mastermind filter check"
  CALL CHECKFILTER(&H0000,&H1234,10000,N(),T(),TL)
  PRINT "Mastermind filter ok"

  PRINT "Normal";GUESS
  CALL CHECKNUMPOOL
  PRINT "Mastermind";GUESSM
  CALL CHECKNUMPOOL

  END
END SUB

SUB CHECKFILTER(ANS AS INTEGER,G AS INTEGER,TL AS INTEGER,SRC() AS INTEGER, DST() AS INTEGER,TLASM AS INTEGER)
  STATIC P,N
  CALL CHECKNUMPOOL
  C=COMP(ANS,G)
  P=0:FOR I=0 TO TL-1
    N=SRC(I):C2=COMP(N,G)
    IF N<>G AND C2=C THEN
      IF N<>DST(P) THEN PRINT "Filter err",HEX$(N),HEX$(DST(P)),I,TLASM:END
      P=P+1
    END IF
  NEXT I
  IF P<>TLASM THEN PRINT "Filter len err";P;TLASM:END
END SUB

SUB CHECKNUMPOOL
  STATIC BCD
  DEF SEG=VARSEG(BCD):P=VARPTR(BCD):P2=0
  FOR I=0 TO 9999
    D0=(I MOD 10):D1=(I\10) MOD 10:D2=(I\100) MOD 10:D3=I\1000
    POKE P,(D1*16) OR D0:POKE P+1,(D3*16) OR D2
    IF N(I)<>BCD THEN PRINT "NumPool corrupt ";HEX$(N(I));" ";HEX$(BCD);I:END
    IF D0<>D1 AND D0<>D2 AND D0<>D3 AND D1<>D2 AND D1<>D3 AND D2<>D3 THEN
      IF NU(P2)<>N(I) THEN PRINT "Unique NumPool corrupt";P;HEX$(NU(P2));I:END
      P2=P2+1
    END IF
  NEXT I
  DEF SEG
END SUB

FUNCTION COMP(A AS INTEGER,B AS INTEGER)
  STATIC A0,B0,A1,B1,R
  DIM C(10,2) AS INTEGER
  DEF SEG=VARSEG(A):A0=PEEK(VARPTR(A)):A1=PEEK(VARPTR(A)+1)
  DEF SEG=VARSEG(B):B0=PEEK(VARPTR(B)):B1=PEEK(VARPTR(B)+1)
  DEF SEG
  R=0
  DA=A0 AND 15:DB=B0 AND 15
  IF DA=DB THEN R=R+16 ELSE C(DA,0)=C(DA,0)+1:C(DB,1)=C(DB,1)+1
  DA=A0\16:DB=B0\16
  IF DA=DB THEN R=R+16 ELSE C(DA,0)=C(DA,0)+1:C(DB,1)=C(DB,1)+1
  DA=A1 AND 15:DB=B1 AND 15
  IF DA=DB THEN R=R+16 ELSE C(DA,0)=C(DA,0)+1:C(DB,1)=C(DB,1)+1
  DA=A1\16:DB=B1\16
  IF DA=DB THEN R=R+16 ELSE C(DA,0)=C(DA,0)+1:C(DB,1)=C(DB,1)+1
  FOR I=0 TO 9:DA=C(I,0):DB=C(I,1)
    IF DA<DB THEN R=R+DA ELSE R=R+DB
  NEXT I
  COMP=R
END FUNCTION

